Dyk ned i JavaScript Temporal Duration, det moderne API til præcis tidsinterval-aritmetik, sammenligning og formatering. Lær at håndtere tidsperioder sikkert og globalt bevidst, og undgå de almindelige faldgruber med Date-objekter.
JavaScript Temporal Duration: Beherskelse af tidsinterval-aritmetik og formatering til globale applikationer
Håndtering af tid i softwareudvikling er notorisk komplekst. Fra sporing af projekt-tidslinjer på tværs af kontinenter til planlægning af internationale videokonferencer kan nuancerne i tidsintervaller, tidszoner og sommertid hurtigt føre til diskrete, men kritiske fejl. I årtier har JavaScript-udviklere kæmpet med det indbyggede Date-objekt, et værktøj, der, selvom det er funktionelt for simple dato-tidspunkter, kommer til kort, når det gælder præcis tidsinterval-aritmetik og robust, globalt bevidst tidshåndtering.
Her kommer JavaScript Temporal API – et banebrydende forslag designet til at levere en moderne, robust og brugervenlig API til at arbejde med datoer og tider i JavaScript. Blandt dets kraftfulde nye typer skiller Temporal.Duration sig ud som den definitive løsning til håndtering af tidsintervaller. Denne artikel vil tage dig med på et dybdegående kig på Temporal.Duration, hvor vi udforsker dets evner inden for aritmetik, sammenligning og intelligent formatering, så dine applikationer kan håndtere tid med global præcision og klarhed.
Uanset om du bygger et globalt logistiksystem, en finansiel handelsplatform eller en eventplanlægger til flere tidszoner, er forståelsen af Temporal.Duration afgørende for at eliminere tidsrelaterede tvetydigheder og levere pålidelige, internationaliserede brugeroplevelser.
Utilstrækkelighederne ved JavaScripts Date-objekt for tidsintervaller
Før vi hylder ankomsten af Temporal.Duration, er det vigtigt at forstå begrænsningerne ved det eksisterende Date-objekt, især når det handler om tidsintervaller. Date-objektet repræsenterer et specifikt tidspunkt, målt i millisekunder siden Unix-epoken (1. januar 1970, UTC). Selvom det kan bruges til at udføre grundlæggende aritmetik, har det flere iboende fejl, der gør det uegnet til robust varighedsstyring:
-
Mutabilitet:
Date-objekter er mutable. Enhver operation på etDate-objekt ændrer dets interne tilstand, hvilket kan føre til uventede bivirkninger og svære at spore fejl, især i komplekse applikationer eller samtidige miljøer.const d = new Date('2023-01-15T10:00:00Z'); const d2 = d; // d2 refererer nu til det samme objekt som d d.setHours(d.getHours() + 1); console.log(d.toISOString()); // 2023-01-15T11:00:00.000Z console.log(d2.toISOString()); // 2023-01-15T11:00:00.000Z (d2 er også ændret!) -
Mangel på et varighedskoncept:
Date-objektet har intet direkte koncept for en "varighed" eller "periode". Beregning af forskellen mellem to datoer resulterer i et antal millisekunder, som derefter manuelt skal konverteres til år, måneder, dage osv. Denne manuelle konvertering er udsat for fejl, især når man håndterer måneder med variabel længde eller skudår.const date1 = new Date('2023-01-01T00:00:00Z'); const date2 = new Date('2023-03-01T00:00:00Z'); const diffMs = date2.getTime() - date1.getTime(); // Hvor mange måneder er dette? Hvad med skudår? // (diffMs / (1000 * 60 * 60 * 24 * 30)) er i bedste fald en tilnærmelse. console.log(`Difference in milliseconds: ${diffMs}`); console.log(`Approximate days: ${diffMs / (1000 * 60 * 60 * 24)}`); // Fungerer for dage, men er ikke robust for måneder/år -
Tidszonetvetydighed:
Date-objekter blander ofte lokal tid og UTC. Selvom de internt gemmer UTC-millisekunder, opererer deres metoder ofte som standard på systemets lokale tidszone, hvilket fører til forvirring og uoverensstemmelser, når man arbejder med distribuerede systemer eller internationale brugere. - Udfordringer med sommertid (DST): DST-overgange kan få dage til at vare 23 eller 25 timer. Simpel aritmetik (f.eks. at tilføje 24 timer til en dato) resulterer måske ikke altid i den næste kalenderdag, hvilket fører til forkerte beregninger, når en "dag" antages at være en fast 24-timers periode.
Disse begrænsninger har historisk tvunget udviklere til at stole på tredjepartsbiblioteker som Moment.js eller date-fns, eller til at skrive kompleks, fejlbehæftet brugerdefineret kode for at håndtere tidsintervaller korrekt. Temporal sigter mod at bringe disse funktioner indbygget i JavaScript.
Introduktion til JavaScript Temporal: En moderne tilgang til tid
Temporal API er et omfattende, nyt globalt objekt i JavaScript, designet til at være en moderne erstatning for det forældede Date-objekt. Dets kerneprincipper er immutabilitet, eksplicit tidszonehåndtering og en klar adskillelse af ansvarsområder mellem forskellige tidskoncepter. Temporal introducerer flere nye klasser, der hver især repræsenterer et særskilt aspekt af tid:
Temporal.Instant: Et specifikt, utvetydigt tidspunkt, uafhængigt af enhver kalender eller tidszone (svarende til et Unix-tidsstempel, men med nanosekund-præcision).Temporal.ZonedDateTime: Et specifikt tidspunkt i en bestemt kalender og tidszone. Dette er den mest komplette repræsentation af en specifik dato og tid for en bruger.Temporal.PlainDate: En kalenderdato (år, måned, dag) uden tid eller tidszone.Temporal.PlainTime: Et væg-ur-tidspunkt (time, minut, sekund osv.) uden dato eller tidszone.Temporal.PlainDateTime: En kalenderdato og et væg-ur-tidspunkt samlet, uden tidszone.Temporal.PlainYearMonth: Et specifikt år og måned i et kalendersystem.Temporal.PlainMonthDay: En specifik måned og dag i et kalendersystem.Temporal.Duration: En signeret tidslængde, såsom "5 timer og 30 minutter" eller "2 dage". Dette er vores fokus i denne guide.
Alle Temporal-objekter er immutable, hvilket betyder, at operationer som at tilføje eller trække tid fra skaber nye objekter i stedet for at modificere eksisterende, hvilket forbedrer forudsigeligheden og reducerer fejl.
Forståelse af Temporal.Duration
Et Temporal.Duration repræsenterer en tidslængde. Afgørende er det, at det er uafhængigt af et specifikt start- eller slutpunkt. Det er simpelthen "hvor meget tid" der er gået eller vil gå. Det kan bestå af år, måneder, uger, dage, timer, minutter, sekunder, millisekunder, mikrosekunder og nanosekunder. Hver komponent er et heltal og kan være positiv eller negativ.
For eksempel er "2 timer og 30 minutter" en varighed. "Perioden fra 1. januar til 1. marts" er en varighed mellem to specifikke punkter, som kan *repræsenteres* af et Temporal.Duration, men Duration i sig selv er blot intervallet.
Oprettelse af en Duration
Der er flere ligetil måder at oprette Temporal.Duration-objekter på:
1. Brug af konstruktøren
Konstruktøren giver dig mulighed for at specificere hver komponent direkte. Bemærk, at argumenterne er sorteret fra den største enhed (år) til den mindste (nanosekunder).
// new Temporal.Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds)
// En varighed på 2 timer og 30 minutter
const duration1 = new Temporal.Duration(0, 0, 0, 0, 2, 30, 0, 0, 0, 0);
console.log(duration1.toString()); // P2H30M
// En varighed på 1 år, 2 måneder, 3 dage
const duration2 = new Temporal.Duration(1, 2, 0, 3);
console.log(duration2.toString()); // P1Y2M3D
// En varighed på -5 dage
const duration3 = new Temporal.Duration(0, 0, 0, -5);
console.log(duration3.toString()); // P-5D
2. Brug af Temporal.Duration.from() med et objekt
Dette er ofte den mest læsbare måde at oprette varigheder på, da det giver dig mulighed for kun at specificere de komponenter, du har brug for.
// Varighed på 1,5 timer
const halfHourDuration = Temporal.Duration.from({ hours: 1, minutes: 30 });
console.log(halfHourDuration.toString()); // P1H30M
// Varighed på 7 dage (1 uge)
const oneWeekDuration = Temporal.Duration.from({ days: 7 });
console.log(oneWeekDuration.toString()); // P7D
// Varighed med brøkdele af sekunder (f.eks. 2,5 sekunder)
const twoPointFiveSeconds = Temporal.Duration.from({ seconds: 2, milliseconds: 500 });
console.log(twoPointFiveSeconds.toString()); // PT2.5S
// Negativ varighed
const negativeDuration = Temporal.Duration.from({ minutes: -45 });
console.log(negativeDuration.toString()); // PT-45M
3. Brug af Temporal.Duration.from() med en ISO 8601-streng
Temporal udnytter ISO 8601-varighedsformatet, som er en standard til repræsentation af varigheder. Dette er fremragende til at parse varigheder fra eksterne datakilder.
Formatet ser generelt således ud: P[years]Y[months]M[weeks]W[days]DT[hours]H[minutes]M[seconds]S. T adskiller datokomponenter fra tidskomponenter.
// 1 år, 2 måneder, 3 dage
const isoDuration1 = Temporal.Duration.from('P1Y2M3D');
console.log(isoDuration1.toString()); // P1Y2M3D
// 4 timer, 5 minutter, 6 sekunder
const isoDuration2 = Temporal.Duration.from('PT4H5M6S');
console.log(isoDuration2.toString()); // PT4H5M6S
// En kombineret varighed
const isoDuration3 = Temporal.Duration.from('P7DT12H30M');
console.log(isoDuration3.toString()); // P7DT12H30M
// Brøkdele af sekunder understøttes også
const isoDuration4 = Temporal.Duration.from('PT1.5S');
console.log(isoDuration4.toString()); // PT1.5S
Udførelse af aritmetik med varigheder
Den sande styrke ved Temporal.Duration skinner igennem i dens aritmetiske evner. Du kan addere, subtrahere, multiplicere og dividere varigheder, og også addere/subtrahere dem fra andre Temporal dato-tid-typer. Alle operationer returnerer nye Temporal.Duration-objekter på grund af immutabilitet.
Addition af varigheder
Metoden add() kombinerer to varigheder.
const sprintDuration = Temporal.Duration.from({ weeks: 2 });
const bufferDuration = Temporal.Duration.from({ days: 3 });
const totalProjectTime = sprintDuration.add(bufferDuration);
console.log(totalProjectTime.toString()); // P2W3D (eller P17D hvis normaliseret senere)
// At tilføje en negativ varighed svarer til subtraktion
const result = Temporal.Duration.from({ hours: 5 }).add({ hours: -2 });
console.log(result.toString()); // PT3H
Du kan også tilføje en varighed til ethvert Temporal dato/tid-objekt. Det er her, magien sker, da Temporal korrekt håndterer tidszoneskift og DST-overgange, når det er relevant.
const projectStart = Temporal.PlainDateTime.from('2023-10-26T09:00:00');
const projectDuration = Temporal.Duration.from({ days: 10, hours: 4 });
const projectEnd = projectStart.add(projectDuration);
console.log(projectEnd.toString()); // 2023-11-05T13:00:00
// Med et ZonedDateTime anvendes tidszonereglerne korrekt
const meetingStartUTC = Temporal.ZonedDateTime.from('2024-03-09T14:00:00[UTC]');
const meetingDuration = Temporal.Duration.from({ hours: 1, minutes: 45 });
const meetingEndUTC = meetingStartUTC.add(meetingDuration);
console.log(meetingEndUTC.toString()); // 2024-03-09T15:45:00+00:00[UTC]
// Eksempel, der krydser en sommertidsgrænse (antager, at 'Europe/Berlin' skifter kl. 03:00 den 31-03-2024)
const springForwardStart = Temporal.ZonedDateTime.from('2024-03-30T22:00:00[Europe/Berlin]');
const twentyFourHours = Temporal.Duration.from({ hours: 24 });
const nextDay = springForwardStart.add(twentyFourHours); // Tilføjer 24 faktiske timer
console.log(springForwardStart.toString()); // 2024-03-30T22:00:00+01:00[Europe/Berlin]
console.log(nextDay.toString()); // 2024-03-31T23:00:00+02:00[Europe/Berlin] (Lokal tid sprang en time over)
Bemærk, hvordan tilføjelsen af 24 timer til 2024-03-30T22:00:00 i Berlin (som er UTC+1) resulterer i 2024-03-31T23:00:00 (nu UTC+2). Uret sprang en time frem, så væg-ur-tiden er en time senere på samme dato i forhold til start-væg-ur-tiden. Dette demonstrerer præcist Temporals bevidsthed om tidszoner og sommertid, når der udføres aritmetik på `ZonedDateTime`.
Subtraktion af varigheder
Metoden subtract() fungerer på samme måde som add(), men den fjerner tid.
const deadlineDuration = Temporal.Duration.from({ days: 30 });
const gracePeriod = Temporal.Duration.from({ days: 5 });
const effectiveDeadline = deadlineDuration.subtract(gracePeriod);
console.log(effectiveDeadline.toString()); // P25D
const taskEnd = Temporal.PlainDateTime.from('2023-12-01T17:00:00');
const taskDuration = Temporal.Duration.from({ hours: 8, minutes: 30 });
const taskStart = taskEnd.subtract(taskDuration);
console.log(taskStart.toString()); // 2023-12-01T08:30:00
Multiplikation og division af varigheder
Metoderne multiply() og divide() skalerer komponenterne i en varighed med en given faktor. Dette er nyttigt i scenarier som beregning af den samlede tid for flere gentagelser af en opgave.
const trainingSession = Temporal.Duration.from({ minutes: 45 });
const weeklyTraining = trainingSession.multiply(5); // Fem sessioner om ugen
console.log(weeklyTraining.toString()); // PT225M
const totalProjectHours = Temporal.Duration.from({ hours: 160 });
const teamMembers = 4;
const hoursPerMember = totalProjectHours.divide(teamMembers);
console.log(hoursPerMember.toString()); // PT40H
Negation af varigheder
Metoden negate() vender fortegnet på alle komponenter i en varighed. En positiv varighed bliver negativ, og omvendt.
const delayDuration = Temporal.Duration.from({ hours: 3 });
const advanceDuration = delayDuration.negate();
console.log(delayDuration.toString()); // PT3H
console.log(advanceDuration.toString()); // PT-3H
Absolut værdi af varigheder
Metoden abs() returnerer en ny Temporal.Duration, hvor alle komponenter er gjort positive, hvilket effektivt giver dig størrelsen af varigheden uanset dens fortegn.
const negativeDelay = Temporal.Duration.from({ minutes: -60 });
const positiveDuration = negativeDelay.abs();
console.log(negativeDelay.toString()); // PT-60M
console.log(positiveDuration.toString()); // PT60M
Sammenligning og normalisering af varigheder
Sammenligning af varigheder kan være vanskeligt, især når forskellige enheder er involveret (f.eks. er 1 måned lig med 30 dage?). Temporal leverer værktøjer til både sammenligning og normalisering for at håndtere disse kompleksiteter.
Sammenligning af varigheder med compare()
Den statiske metode Temporal.Duration.compare(duration1, duration2, options) returnerer:
-1hvisduration1er mindre endduration20hvisduration1er lig medduration21hvisduration1er større endduration2
Afgørende er det, at når man sammenligner varigheder, der inkluderer enheder med variabel længde som år, måneder eller uger, skal man ofte angive en relativeTo-option. Denne parameter er et `Temporal.ZonedDateTime` eller `Temporal.PlainDateTime`-objekt, der giver kontekst for, hvordan disse enheder skal fortolkes (f.eks. hvor mange dage der er i en bestemt måned eller år).
const oneHour = Temporal.Duration.from({ hours: 1 });
const sixtyMinutes = Temporal.Duration.from({ minutes: 60 });
console.log(Temporal.Duration.compare(oneHour, sixtyMinutes)); // 0 (De er ækvivalente)
const oneMonth = Temporal.Duration.from({ months: 1 });
const thirtyDays = Temporal.Duration.from({ days: 30 });
// Uden relativeTo er sammenligninger af måneder/år vanskelige
console.log(Temporal.Duration.compare(oneMonth, thirtyDays)); // 0 (Temporal laver et rimeligt gæt uden kontekst, ofte baseret på gennemsnit)
// Med relativeTo er sammenligningen præcis baseret på kontekstens kalender
const startOfJanuary = Temporal.PlainDate.from('2023-01-01');
const endOfFebruaryLeap = Temporal.PlainDate.from('2024-02-01'); // Skudår
// I januar 2023 er 1 måned 31 dage
const comparisonJan = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: startOfJanuary });
console.log(`1 month vs 30 days in Jan 2023: ${comparisonJan}`); // 1 (1 måned > 30 dage)
// I februar 2024 (skudår) er 1 måned 29 dage
const comparisonFeb = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: endOfFebruaryLeap });
console.log(`1 month vs 30 days in Feb 2024: ${comparisonFeb}`); // -1 (1 måned < 30 dage)
Normalisering af varigheder med normalize() og round()
Varigheder kan repræsenteres på mange måder (f.eks. 90 minutter eller 1 time og 30 minutter). Normalisering og afrunding hjælper med at standardisere disse repræsentationer for konsistens og visning.
normalize()
Metoden normalize() forenkler varighedskomponenter, hvor det er muligt (f.eks. bliver 60 minutter til 1 time, 24 timer bliver til 1 dag, forudsat at `relativeTo`-konteksten tillader det, hvis måneder/år er involveret).
const longMinutes = Temporal.Duration.from({ minutes: 90 });
console.log(longMinutes.toString()); // PT90M
console.log(longMinutes.normalize().toString()); // PT1H30M
const multipleDays = Temporal.Duration.from({ hours: 48 });
console.log(multipleDays.toString()); // PT48H
console.log(multipleDays.normalize().toString()); // P2D
round()
Metoden round() er mere kraftfuld til at transformere og afrunde varigheder til specifikke enheder. Den tager et options-objekt med:
largestUnit: Den største enhed, der skal inkluderes i outputtet (f.eks. 'years', 'days', 'hours').smallestUnit: Den mindste enhed, der skal inkluderes i outputtet (f.eks. 'minutes', 'seconds', 'milliseconds').roundingIncrement: Et heltal, som den mindste enhed skal afrundes med (f.eks. 5 for afrunding til nærmeste 5 minutter).roundingMode: Hvordan uafgjorte tilfælde skal håndteres (f.eks. 'halfExpand', 'trunc', 'ceil', 'floor').relativeTo: Påkrævet for afrunding af varigheder, der indeholder år, måneder eller uger.
const complexDuration = Temporal.Duration.from({ hours: 2, minutes: 45, seconds: 30 });
// Rund til nærmeste time
const roundedToHours = complexDuration.round({ smallestUnit: 'hour' });
console.log(roundedToHours.toString()); // PT3H
// Rund til nærmeste 30 minutter, og behold timer
const roundedTo30Minutes = complexDuration.round({
largestUnit: 'hour',
smallestUnit: 'minute',
roundingIncrement: 30
});
console.log(roundedTo30Minutes.toString()); // PT3H
const preciseDuration = Temporal.Duration.from({ minutes: 123, seconds: 45 });
// Vis som timer og minutter
const formattedDuration = preciseDuration.round({ largestUnit: 'hour', smallestUnit: 'minute' });
console.log(formattedDuration.toString()); // PT2H4M
// Afrunding med måneder/år kræver relativeTo
const longTermDuration = Temporal.Duration.from({ months: 1, days: 10 });
const referenceDate = Temporal.PlainDate.from('2023-01-15');
// Rund til måneder, relativt til en dato
const roundedToMonths = longTermDuration.round({ largestUnit: 'month', smallestUnit: 'month', relativeTo: referenceDate });
console.log(roundedToMonths.toString()); // P1M
Beregning af varigheder mellem Temporal-objekter
En af de hyppigste anvendelser af varigheder er at beregne tidsintervallet mellem to specifikke tidspunkter. Temporal tilbyder metoderne until() og since() på sine dato-tid-objekter til dette formål.
until()-metoden
Metoden until() beregner varigheden fra modtagerobjektet til argumentobjektet. Den er inklusiv startpunktet og eksklusiv slutpunktet. Den tager et options-objekt, der ligner round(), til at specificere de ønskede enheder og afrundingsadfærd.
const startDate = Temporal.PlainDate.from('2023-01-01');
const endDate = Temporal.PlainDate.from('2023-03-15');
// Varighed i størst mulige enheder (måneder, derefter dage)
const projectLength = startDate.until(endDate);
console.log(projectLength.toString()); // P2M14D
// Varighed udelukkende i dage
const totalDays = startDate.until(endDate, { largestUnit: 'day' });
console.log(totalDays.toString()); // P73D
// Varighed mellem to specifikke tidspunkter, der respekterer tidszoner
const meetingStart = Temporal.ZonedDateTime.from('2024-07-20T10:00:00[America/New_York]');
const meetingEnd = Temporal.ZonedDateTime.from('2024-07-20T11:30:00[America/New_York]');
const elapsedMeetingTime = meetingStart.until(meetingEnd, { largestUnit: 'hour', smallestUnit: 'minute' });
console.log(elapsedMeetingTime.toString()); // PT1H30M
// Varighed på tværs af tidszoner (fra NYC til London)
const nyStartTime = Temporal.ZonedDateTime.from('2024-08-01T09:00:00[America/New_York]');
const londonEndTime = Temporal.ZonedDateTime.from('2024-08-01T17:00:00[Europe/London]');
const travelDuration = nyStartTime.until(londonEndTime);
console.log(travelDuration.toString()); // PT13H (Faktisk forløbet tid, ikke forskellen i væg-ur-tid)
Det sidste eksempel er særligt indsigtsfuldt. Selvom New York er 5 timer bagud i forhold til London, og væg-ur-tiderne er kl. 9 og kl. 17 på samme dag, beregner until()-metoden korrekt den faktiske forløbne tid på 13 timer. Dette skyldes, at ZonedDateTime implicit håndterer tidszoneforskellen.
since()-metoden
Metoden since() er det modsatte af until(). Den beregner varigheden fra argumentobjektet til modtagerobjektet, hvilket resulterer i en negativ varighed, hvis argumentet ligger i fremtiden i forhold til modtageren.
const currentDateTime = Temporal.ZonedDateTime.from('2024-06-15T12:00:00[Europe/Paris]');
const historicEvent = Temporal.ZonedDateTime.from('2024-01-01T00:00:00[Europe/Paris]');
const timeSinceEvent = currentDateTime.since(historicEvent, { largestUnit: 'month', smallestUnit: 'day' });
console.log(timeSinceEvent.toString()); // P5M14D
const futureDate = Temporal.PlainDate.from('2025-01-01');
const pastDate = Temporal.PlainDate.from('2024-01-01');
const durationFromFuture = pastDate.since(futureDate);
console.log(durationFromFuture.toString()); // P-1Y
Håndtering af forskellige enheder og afrunding for beregnede varigheder
Ved beregning af varigheder er det ofte nødvendigt at specificere `largestUnit` og `smallestUnit` for at få en menneskeligt læsbar repræsentation, især for alder, forløbet tid eller nedtællinger.
const birthDate = Temporal.PlainDate.from('1990-07-15');
const today = Temporal.PlainDate.from('2024-06-15');
// Beregn alder i år, måneder og dage
const age = birthDate.until(today, { largestUnit: 'year', smallestUnit: 'day' });
console.log(`Age: ${age.years} years, ${age.months} months, ${age.days} days`); // Alder: 33 år, 11 måneder, 0 dage
// Beregn resterende tid for en opgave i timer og minutter
const now = Temporal.Instant.fromEpochSeconds(Date.now() / 1000);
const deadline = Temporal.Instant.from('2024-07-01T09:00:00Z');
const timeLeft = now.until(deadline, { largestUnit: 'hour', smallestUnit: 'minute', roundingMode: 'ceil' });
console.log(`Time left: ${timeLeft.hours} hours and ${timeLeft.minutes} minutes.`); // Eksempel: Tid tilbage: 355 timer og 38 minutter.
Formatering af varigheder for et globalt publikum
Selvom Temporal.Duration giver præcise, programmatiske repræsentationer af tidsintervaller, har den ikke en indbygget toLocaleString()-metode. Dette er bevidst: varigheder er abstrakte tidslængder, og deres visning kan variere vildt afhængigt af kontekst, lokalitet og ønsket detaljeringsgrad. Det er dig, som udvikler, der er ansvarlig for at præsentere varigheder på en brugervenlig, globalt forståelig måde.
ISO 8601-strengrepræsentation
Standard toString()-metoden for et Temporal.Duration-objekt returnerer dets ISO 8601-strengrepræsentation. Dette er fremragende til maskine-til-maskine-kommunikation, serialisering og lagring, men sjældent til direkte visning for slutbrugere.
const examDuration = Temporal.Duration.from({ hours: 2, minutes: 15 });
console.log(examDuration.toString()); // PT2H15M
const holidayDuration = Temporal.Duration.from({ weeks: 2, days: 3 });
console.log(holidayDuration.toString()); // P2W3D
Manuel formatering for læsbarhed og internationalisering
Til brugerrettet visning vil du typisk udtrække komponenterne fra en varighed og formatere dem ved hjælp af streng-interpolation og JavaScripts Intl API.
Her er et eksempel på en brugerdefineret funktion, der formaterer en varighed:
function formatDurationToHumanReadable(duration, locale = 'en-US') {
const parts = [];
// Bruger Intl.NumberFormat til lokalitetsbevidst talformatering
const numberFormatter = new Intl.NumberFormat(locale);
if (duration.years !== 0) {
parts.push(numberFormatter.format(duration.years) + ' ' + (duration.years === 1 ? 'år' : 'år'));
}
if (duration.months !== 0) {
parts.push(numberFormatter.format(duration.months) + ' ' + (duration.months === 1 ? 'måned' : 'måneder'));
}
if (duration.weeks !== 0) {
parts.push(numberFormatter.format(duration.weeks) + ' ' + (duration.weeks === 1 ? 'uge' : 'uger'));
}
if (duration.days !== 0) {
parts.push(numberFormatter.format(duration.days) + ' ' + (duration.days === 1 ? 'dag' : 'dage'));
}
if (duration.hours !== 0) {
parts.push(numberFormatter.format(duration.hours) + ' ' + (duration.hours === 1 ? 'time' : 'timer'));
}
if (duration.minutes !== 0) {
parts.push(numberFormatter.format(duration.minutes) + ' ' + (duration.minutes === 1 ? 'minut' : 'minutter'));
}
if (duration.seconds !== 0) {
// Afrund sekunder til visning, hvis de har brøkdele
const roundedSeconds = numberFormatter.format(duration.seconds.toFixed(0)); // Eller toFixed(1) for én decimal
parts.push(roundedSeconds + ' ' + (duration.seconds === 1 ? 'sekund' : 'sekunder'));
}
if (parts.length === 0) {
// Håndter tilfælde, hvor varigheden er nul eller meget lille (f.eks. kun nanosekunder)
if (duration.milliseconds !== 0 || duration.microseconds !== 0 || duration.nanoseconds !== 0) {
const totalMs = duration.milliseconds + duration.microseconds / 1000 + duration.nanoseconds / 1_000_000;
return numberFormatter.format(totalMs.toFixed(2)) + ' millisekunder';
}
return '0 sekunder';
}
// Sammenføj dele med komma og 'og' for den sidste del (grundlæggende dansk sammenføjning)
if (parts.length > 1) {
const lastPart = parts.pop();
return parts.join(', ') + ' og ' + lastPart;
} else {
return parts[0];
}
}
const tripDuration = Temporal.Duration.from({ days: 3, hours: 10, minutes: 45 });
console.log(formatDurationToHumanReadable(tripDuration, 'da-DK')); // 3 dage, 10 timer og 45 minutter
console.log(formatDurationToHumanReadable(tripDuration, 'da-DK')); // 3 dage, 10 timer og 45 minutter (eksempel på Intl.NumberFormat)
const meetingReminder = Temporal.Duration.from({ minutes: 5 });
console.log(formatDurationToHumanReadable(meetingReminder, 'en-GB')); // 5 minutter
const microDuration = Temporal.Duration.from({ nanoseconds: 1234567 });
console.log(formatDurationToHumanReadable(microDuration, 'en-US')); // 1,23 millisekunder
For mere avanceret pluralisering og lokaliseret listeformatering kan du kombinere dette med Intl.RelativeTimeFormat eller mere komplekse streng-templating-biblioteker. Nøglen er at adskille varighedsberegningen (håndteret af Temporal) fra dens præsentation (håndteret af din formateringslogik og Intl).
Brug af Intl.DurationFormat (Fremtid/Forslag)
Der er et igangværende TC39-forslag til Intl.DurationFormat, som sigter mod at tilbyde en indbygget, lokalitetsbevidst måde at formatere varigheder på. Hvis det bliver standardiseret og implementeret, vil det i høj grad forenkle den manuelle formatering vist ovenfor og tilbyde en robust løsning til internationalisering direkte i JavaScript-økosystemet.
Det vil sandsynligvis fungere på samme måde som andre Intl-objekter:
// Dette er hypotetisk og baseret på forslagets nuværende status
// Tjek browserkompatibilitet før brug i produktion
/*
const duration = Temporal.Duration.from({ days: 1, hours: 2, minutes: 30 });
const formatter = new Intl.DurationFormat('da-DK', {
style: 'long',
years: 'long',
days: 'long',
hours: 'long',
minutes: 'long',
});
console.log(formatter.format(duration)); // "1 dag, 2 timer, 30 minutter"
const shortFormatter = new Intl.DurationFormat('fr-FR', { style: 'short', hours: 'numeric', minutes: 'numeric' });
console.log(shortFormatter.format(duration)); // "1 d, 2 t, 30 min"
*/
Selvom det endnu ikke er tilgængeligt i alle miljøer, så hold øje med dette forslag, da det lover at blive den definitive løsning til global varighedsformatering i fremtiden.
Praktiske anvendelsestilfælde og globale overvejelser
Temporal.Duration er ikke kun en akademisk forbedring; det løser virkelige problemer i applikationer, der opererer på tværs af forskellige tidszoner og kulturer.
1. Planlægning og eventstyring
For platforme, der administrerer internationale events, konferencer eller aftaler, er det afgørende at beregne varigheden af events, tiden indtil et event, eller hvor længe en pause varer. Temporal.Duration sikrer, at disse beregninger er nøjagtige uanset brugerens placering eller lokale tidszoneregler.
// Beregn resterende tid til et globalt webinar
const now = Temporal.ZonedDateTime.from('2024-07-25T10:00:00[Europe/London]'); // Eksempel på nuværende tid
const webinarStart = Temporal.ZonedDateTime.from('2024-07-26T14:30:00[Asia/Tokyo]');
const timeUntilWebinar = now.until(webinarStart, {
largestUnit: 'hour',
smallestUnit: 'minute',
roundingMode: 'ceil' // Rund op for at sikre, at brugerne ikke går glip af det
});
console.log(`Webinar starts in ${timeUntilWebinar.hours} hours and ${timeUntilWebinar.minutes} minutes.`);
// Output vil være nøjagtigt baseret på den faktiske forløbne tid mellem disse to ZonedDateTimes.
2. Performance-metrikker og logning
Måling af udførelsestiden for operationer, API-svartider eller varigheden af batch-jobs kræver høj præcision. Temporal.Duration, især når det kombineres med Temporal.Instant (som tilbyder nanosekund-præcision), er ideel til dette.
const startTime = Temporal.Instant.now();
// Simuler en kompleks operation
for (let i = 0; i < 1_000_000; i++) { Math.sqrt(i); }
const endTime = Temporal.Instant.now();
const executionDuration = startTime.until(endTime);
// Formater til sekunder med millisekunder til visning
const formattedExecution = executionDuration.round({ smallestUnit: 'millisecond', largestUnit: 'second' });
console.log(`Operation took ${formattedExecution.seconds}.${String(formattedExecution.milliseconds).padStart(3, '0')} seconds.`);
3. Finansielle beregninger
Inden for finans er præcise tidsintervaller altafgørende for beregning af renter, lånevilkår eller investeringsperioder. Det nøjagtige antal dage, måneder eller år i en periode skal være korrekt, især når man har med skudår eller specifikke månedslængder at gøre.
const loanStartDate = Temporal.PlainDate.from('2023-04-01');
const loanEndDate = Temporal.PlainDate.from('2028-03-31');
const loanTerm = loanStartDate.until(loanEndDate, { largestUnit: 'year', smallestUnit: 'month' });
console.log(`Loan term: ${loanTerm.years} years and ${loanTerm.months} months.`); // Låneperiode: 4 år og 11 måneder
4. Internationaliseringsudfordringer genbesøgt
-
Tidszoner og sommertid:
Temporal.Durationer i sig selv tidszone-agnostisk; det repræsenterer en fast tidslængde. Men når du adderer eller subtraherer enDurationtil/fra enZonedDateTime, anvender Temporal korrekt tidszoneregler, inklusive sommertidsskift. Dette sikrer, at en "24-timers varighed" virkelig rykker enZonedDateTime24 faktiske timer frem, selvom det betyder at krydse en sommertidsgrænse, der gør *væg-ur*-dagen kortere eller længere. Denne konsistens er en stor sejr over `Date`. -
Kulturelle variationer: Forskellige kulturer udtrykker varigheder forskelligt. Selvom
Temporal.Durationgiver de rå komponenter (år, måneder, dage osv.), er det dit ansvar at bruge `Intl`-API'er eller brugerdefineret logik til at præsentere disse på en måde, der giver mening for brugerens lokalitet. For eksempel kan nogle kulturer udtrykke "1 time og 30 minutter" som "90 minutter" eller bruge forskellige flertalsregler. -
Kalendersystemer: Temporal understøtter også forskellige kalendersystemer (f.eks. japanske, persiske, islamiske kalendere). Selvom
Durationi sig selv er kalender-agnostisk, respekterer aritmetikken den specifikke kalenders regler (f.eks. antal dage i en måned, skudårsregler inden for den kalender), når den interagerer med kalenderbevidste typer somPlainDateellerZonedDateTime. Dette er afgørende for globale applikationer, der muligvis skal vise datoer i ikke-gregorianske kalendere.
Temporals nuværende status og udbredelse
Fra slutningen af 2023/begyndelsen af 2024 er JavaScript Temporal API et Stage 3 TC39-forslag. Det betyder, at specifikationen er stort set stabil, og den er under implementering og test i forskellige JavaScript-motorer og browsere. Store browsere som Chrome, Firefox og Safari arbejder aktivt på at implementere Temporal, med eksperimentelle flag eller tidlige versioner allerede tilgængelige.
Selvom det endnu ikke er universelt tilgængeligt uden en polyfill, indikerer dets fremskredne stadie, at det er meget sandsynligt, at det bliver en standarddel af JavaScript. Udviklere kan begynde at eksperimentere med Temporal i dag ved at bruge polyfills eller ved at aktivere eksperimentelle funktioner i deres browserudviklingsmiljøer. Tidlig adoption giver dig mulighed for at komme foran, forstå fordelene og integrere det i dine projekter, efterhånden som browserunderstøttelsen rulles ud.
Dets endelige udbredelse vil markant reducere behovet for eksterne dato/tid-biblioteker og give en robust, indbygget løsning til JavaScript-tidshåndtering.
Bedste praksis for brug af Temporal Duration
For at maksimere fordelene ved Temporal.Duration i dine applikationer, overvej disse bedste praksisser:
-
Foretræk
Temporal.Duration.from(): Når du opretter varigheder fra kendte komponenter eller ISO-strenge, erTemporal.Duration.from()med et objekt-literal ofte mere læsbart og mindre fejlbehæftet end konstruktørens positionelle argumenter. -
Brug
relativeTotil tvetydige sammenligninger: Angiv altid enrelativeTo-option, når du sammenligner eller afrunder varigheder, der indeholder år, måneder eller uger. Dette sikrer nøjagtige beregninger ved at give den nødvendige kalenderkontekst. -
Udnyt
until()ogsince(): Til beregning af intervallet mellem to specifikke tidspunkter, foretræk metoderneuntil()ogsince()på Temporal dato-tid-objekter. De håndterer kompleksiteten af tidszoner og sommertid korrekt. -
Normaliser og afrund til visning: Før du præsenterer varigheder for brugere, overvej at bruge
normalize()og isærround()til at konvertere varigheden til de mest passende og forståelige enheder (f.eks. konvertere 90 minutter til "1 time og 30 minutter"). -
Adskil intern repræsentation fra visning: Hold dine interne varighedsberegninger præcise med
Temporal.Duration. Transformer og formater kun til visning, når du gengiver brugergrænsefladen, ved hjælp af brugerdefinerede formateringsfunktioner ogIntl-API'en for global nøjagtighed. - Hold dig opdateret: Hold øje med Temporal API'ens fremskridt og browserkompatibilitet. Efterhånden som det bevæger sig mod fuld standardisering, vil økosystemet udvikle sig, og nye værktøjer eller bedste praksisser kan opstå.
- Vær opmærksom på præcision: Selvom Temporal understøtter nanosekund-præcision, så brug kun den præcision, du virkelig har brug for. Højere præcision kan undertiden gøre fejlfinding sværere eller resultere i uventet afrunding ved konvertering til visninger med lavere præcision.
Konklusion
Introduktionen af Temporal.Duration markerer et betydeligt spring fremad for JavaScript-udviklere, der kæmper med tidsinterval-aritmetik. Ved at tilbyde en immutable, eksplicit og globalt bevidst API, adresserer Temporal de mangeårige begrænsninger i det forældede Date-objekt.
Du kan nu med sikkerhed udføre komplekse tidsberegninger, nøjagtigt måle perioder og præsentere varigheder på en måde, der er både præcis og kulturelt passende for brugere over hele verden. Uanset om du beregner varigheden af en software-release-cyklus, den resterende tid indtil en global produktlancering eller en persons nøjagtige alder, tilbyder Temporal.Duration de værktøjer, du har brug for til at bygge robuste og pålidelige applikationer.
Omfavn Temporal.Duration og transformer, hvordan du håndterer tid i dine JavaScript-projekter. Fremtiden for dato- og tidshåndtering i JavaScript er her, og den lover en mere forudsigelig, kraftfuld og globalt kompatibel udviklingsoplevelse.